{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright (c) MONAI Consortium  \n",
    "Licensed under the Apache License, Version 2.0 (the \"License\");  \n",
    "you may not use this file except in compliance with the License.  \n",
    "You may obtain a copy of the License at  \n",
    "&nbsp;&nbsp;&nbsp;&nbsp;http://www.apache.org/licenses/LICENSE-2.0  \n",
    "Unless required by applicable law or agreed to in writing, software  \n",
    "distributed under the License is distributed on an \"AS IS\" BASIS,  \n",
    "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  \n",
    "See the License for the specific language governing permissions and  \n",
    "limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "WLbIJbxGDC95"
   },
   "source": [
    "# MONAI for PyTorch users\n",
    "This tutorial briefly introduces MONAI APIs and highlights its flexibility and usability. It assumes basic understanding of PyTorch, and shows how MONAI provide domain-optimized capabilities for deep learning in healthcare imaging."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mQGI9QnZE37_"
   },
   "source": [
    "## Setup environment\n",
    "\n",
    "MONAI's core functionality is written in Python and only depends on Numpy and Pytorch.\n",
    "\n",
    "This section installs the latest version of MONAI and verifies the installation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "G8pKPruSCe0e",
    "outputId": "0e6d0b3d-16fd-4dd0-82b9-920555533f69"
   },
   "outputs": [],
   "source": [
    "!python -c \"import monai\" || pip install -q \"monai-weekly[tqdm, nibabel, gdown, ignite]\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "8Akply-HeKHm",
    "outputId": "fd40a7be-dd32-462c-8ebc-d367231670aa"
   },
   "outputs": [],
   "source": [
    "import monai.transforms as mt\n",
    "import matplotlib.pyplot as plt\n",
    "import ignite\n",
    "import numpy as np\n",
    "import torch\n",
    "import monai\n",
    "import warnings\n",
    "\n",
    "warnings.filterwarnings(\"ignore\")  # remove some scikit-image warnings\n",
    "\n",
    "monai.config.print_config()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yV7SEzjDF9-Q"
   },
   "source": [
    "## Access public medical imaging dataset in a few lines of code\n",
    "Using a publicly available benchmark is important for open and reproducible research. MONAI aims at providing quick access to the well-known public datasets. This section starts a dataset of [Medical Segmentation Decathlon](http://medicaldecathlon.com/).\n",
    "\n",
    "The `DecathlonDataset` object is a thin wrapper of `torch.data.utils.Dataset`. \n",
    "It has  `__getitem__` and `__len__` methods implemented, fully compatible with Pytorch's build-in data loader `torch.data.utils.DataLoader`.\n",
    "\n",
    "Compared with the PyTorch `Dataset` APIs, `DecathlonDataset` has the additional capabilities of\n",
    "- automatic downloading and unzipping data\n",
    "- caching of data as well as intermediate results of preprocessing\n",
    "- random splits of training, validation, and test \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "wLSWNutZIDii",
    "outputId": "83fe07cb-6fd3-498a-a6e4-3aedae8824c5"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2023-02-25 17:10:26,048 - INFO - Verified 'Task05_Prostate.tar', md5: 35138f08b1efaef89d7424d2bcc928db.\n",
      "2023-02-25 17:10:26,048 - INFO - File exists: Task05_Prostate.tar, skipped downloading.\n",
      "2023-02-25 17:10:26,050 - INFO - Non-empty folder exists in Task05_Prostate, skipped extracting.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Loading dataset: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:00<00:00, 257197.89it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "number of subjects: 26\n",
      "The first element in the dataset is\n",
      "{'image': 'Task05_Prostate/imagesTr/prostate_46.nii.gz', 'label': 'Task05_Prostate/labelsTr/prostate_46.nii.gz'}.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "dataset = monai.apps.DecathlonDataset(\n",
    "    root_dir=\"./\", task=\"Task05_Prostate\", section=\"training\", transform=None, download=True\n",
    ")\n",
    "print(f\"\\nnumber of subjects: {len(dataset)}\")\n",
    "print(f\"The first element in the dataset is\\n{dataset[0]}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "POwC6_uvLFtt"
   },
   "source": [
    "The code section has created a `DecathlonDataset` object by downloading \"Task05_Prostate.tar\" (`download=True`) from the public repository, unzipping it, and parsing the JSON file provided by the archive.\n",
    "\n",
    "This is a three-class segmentation task for delineating prostate transitional zone and peripheral zone (background, TZ, PZ classes).  The input has two modalities, that is, T2 and ADC MRI."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "3yRel2e3hxLo"
   },
   "source": [
    "`len(dataset)` and `dataset[0]` queries the size of the dataset, and fetches the first element of the dataset respectively.  As we haven't specified any image transforms for the dataset, the outputs of this iterable dataset are just pairs of image and segmentation file names."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_aMAATwVKmJy"
   },
   "source": [
    "## Flexible image data transformations\n",
    "This section introduces MONAI transforms that convert the file names into data arrays in memory, which will be ready to be consumed by the deep learning models. More examples on the pre-processing pipelines are available in the [tutorials repository](https://github.com/Project-MONAI/tutorials):\n",
    "\n",
    "- [3d_image_transforms](3d_image_transforms.ipynb)\n",
    "- [transforms_update_meta_data](transforms_update_meta_data.ipynb)\n",
    "- [integrate_3rd_party_transforms](integrate_3rd_party_transforms.ipynb)\n",
    "\n",
    "Here we briefly cover the main features for image pre-processing."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "s7xMT2cEjJbx"
   },
   "source": [
    "### Array vs. Dictionary-based transforms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vtruH9sjmZBI"
   },
   "source": [
    "Array transforms are basic building blocks in MONAI, it is a simple callable similar to `torchvision.transforms`. Except for two differences:\n",
    "- MONAI transforms implements medical imaging specific processing functionality\n",
    "- MONAI transforms assumes that the inputs are numpy array, pytorch tensors or tensor-like objects\n",
    "\n",
    "The following starts a loader transform which converts a filename string into the actual image data.\n",
    "\n",
    "Please refer to [the documentation for more options](https://docs.monai.io/en/latest/transforms.html#monai.transforms.LoadImage) to this transform."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "TE5Q8axDjHRQ",
    "outputId": "97d6842c-bd73-4e37-8990-c766d8fb651b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([320, 320, 24, 2])\n"
     ]
    }
   ],
   "source": [
    "loader = mt.LoadImage(image_only=True)\n",
    "img_array = loader(\"Task05_Prostate/imagesTr/prostate_02.nii.gz\")\n",
    "print(img_array.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "AO-O5ZXFngUh"
   },
   "source": [
    "Dictionary transforms are nothing but wrappers of the array versions. Compared with their array-based counterparts, it's easier to apply the same type of operations or states to multiple data inputs.\n",
    "\n",
    "The following section reads a pair of image and segmentation mask, note that the `keys` need to be specified, so that the transforms know which items in the dictionary should be processed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "RryBbRnwMK8Y",
    "outputId": "88873a7d-7816-42d7-fbd4-0d0c6950ca8b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image shape: torch.Size([320, 320, 24, 2]), \n",
      "label shape: torch.Size([320, 320, 24])\n"
     ]
    }
   ],
   "source": [
    "dict_loader = mt.LoadImageD(keys=(\"image\", \"label\"))\n",
    "data_dict = dict_loader(\n",
    "    {\"image\": \"Task05_Prostate/imagesTr/prostate_02.nii.gz\", \"label\": \"Task05_Prostate/labelsTr/prostate_02.nii.gz\"}\n",
    ")\n",
    "print(f\"image shape: {data_dict['image'].shape}, \\nlabel shape: {data_dict['label'].shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default, `LoadImage` returns a MONAI `MetaTensor` instance, which is a subclass of PyTorch Tensor.\n",
    "\n",
    "MONAI is in a unique position, leveraging the most advanced machine learning libraries, \n",
    "and focusing on medical image domain-specific features. `MetaTensor` is the core data structure, making the medical image metadata loosely coupled with image array data in a deep learning pipeline.\n",
    "\n",
    "MetaTensor is highly interoperable across Numpy and PyTorch APIs by design, and is supported by all MONAI transforms. For more information please see the tutorials:\n",
    "\n",
    "- [transforms_metatensor](transforms_metatensor.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "x7nKWpKPoNvF"
   },
   "source": [
    "### Compose the transforms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "qqsFxN84opS1"
   },
   "source": [
    "In many cases, it is beneficial to build a chain of transforms, tuning the preprocessing parameters, making fast preprocessing pipelines. `monai.transforms.Compose` is designed for these use cases.\n",
    "\n",
    "The following code starts a transform chain to do multiple pre-processing steps:\n",
    "- Load Nifti images (with image meta-data information)\n",
    "- Make both image and label channel-first (reshape the image and add a channel dimension for the label)\n",
    "- Make both image and label 1 millimeter isotropic\n",
    "- Make both image and label to be in the \"RAS\" coordinate system\n",
    "- Scale the image intensities\n",
    "- Resize both image and label to spatial size (64, 64, 32)-millimeters\n",
    "- Randomly rotate and scale the image, but keep the output size to be (64, 64, 32)-mm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "VdFXzJV-oNEM",
    "outputId": "7a639986-5b6b-4adf-dd8d-fe2790637102"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([2, 64, 64, 32]) torch.Size([1, 64, 64, 32])\n"
     ]
    }
   ],
   "source": [
    "KEYS = (\"image\", \"label\")\n",
    "\n",
    "xform = mt.Compose(\n",
    "    [\n",
    "        mt.LoadImageD(KEYS),\n",
    "        mt.EnsureChannelFirstD(\"image\"),\n",
    "        mt.EnsureChannelFirstD(\"label\", channel_dim=\"no_channel\"),\n",
    "        mt.OrientationD(KEYS, axcodes=\"RAS\"),\n",
    "        mt.SpacingD(KEYS, pixdim=(1.0, 1.0, 1.0), mode=(\"bilinear\", \"nearest\")),\n",
    "        mt.ScaleIntensityD(keys=\"image\"),\n",
    "        mt.ResizeD(KEYS, (64, 64, 32), mode=(\"trilinear\", \"nearest\")),\n",
    "        mt.RandAffineD(\n",
    "            KEYS,\n",
    "            spatial_size=(-1, -1, -1),\n",
    "            rotate_range=(0, 0, np.pi / 2),\n",
    "            scale_range=(0.1, 0.1),\n",
    "            mode=(\"bilinear\", \"nearest\"),\n",
    "            prob=1.0,\n",
    "        ),\n",
    "    ]\n",
    ")\n",
    "\n",
    "data_dict = xform(\n",
    "    {\"image\": \"Task05_Prostate/imagesTr/prostate_02.nii.gz\", \"label\": \"Task05_Prostate/labelsTr/prostate_02.nii.gz\"}\n",
    ")\n",
    "print(data_dict[\"image\"].shape, data_dict[\"label\"].shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Lr91W7y0r-Xi"
   },
   "source": [
    "There are a set of useful transforms in MONAI, and many more to come.\n",
    "Please find out the details at https://docs.monai.io/en/latest/transforms.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "HgSh9o-FsPHG"
   },
   "source": [
    "## Dataset, transform, and data loader\n",
    "To recap what we have:\n",
    "- Dataset: a thin wrapper of `torch.utils.data.Dataset`\n",
    "- Transform: a callable, can be a part of a transform chain made by `Compose`\n",
    "- Setting transforms to a dataset, will enable the data loading and preprocessing pipeline\n",
    "\n",
    "The pipeline can work with the pytorch native data loader, which provides multi-processing support and flexible batch sampling schemes.\n",
    "\n",
    "However, it is recommended to work with the MONAI data loader `monai.data.DataLoader`, which is a wrapper of the pytorch native. MONAI data loader mainly adds the capabilities of:\n",
    "- properly handling randomized augmentations in the multi-process contexts\n",
    "- customized collate functions to flatten multi-sample data list into individual training samples\n",
    "\n",
    "*The initialization of `DecathlonDataset` involves caching the (non-randomizable) transforms' results. It seems to be slow, but when using the initialized object for a training of multiple epochs, we effectively trade the space for much improved speed.*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "849czsBWt5yw",
    "outputId": "229b096d-7533-493e-f496-34c13568c64e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2023-02-25 17:10:27,355 - INFO - Verified 'Task05_Prostate.tar', md5: 35138f08b1efaef89d7424d2bcc928db.\n",
      "2023-02-25 17:10:27,355 - INFO - File exists: Task05_Prostate.tar, skipped downloading.\n",
      "2023-02-25 17:10:27,356 - INFO - Non-empty folder exists in Task05_Prostate, skipped extracting.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Loading dataset: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:10<00:00,  2.40it/s]\n"
     ]
    }
   ],
   "source": [
    "# start a chain of transforms\n",
    "xform = mt.Compose(\n",
    "    [\n",
    "        mt.LoadImageD(KEYS, image_only=True),\n",
    "        mt.EnsureChannelFirstD(\"image\"),\n",
    "        mt.EnsureChannelFirstD(\"label\", channel_dim=\"no_channel\"),\n",
    "        mt.OrientationD(KEYS, axcodes=\"RAS\"),\n",
    "        mt.SpacingD(KEYS, pixdim=(1.0, 1.0, 1.0), mode=(\"bilinear\", \"nearest\")),\n",
    "        mt.ScaleIntensityD(keys=\"image\"),\n",
    "        mt.ResizeD(KEYS, (64, 64, 32), mode=(\"trilinear\", \"nearest\")),\n",
    "        mt.RandAffineD(\n",
    "            KEYS,\n",
    "            spatial_size=(-1, -1, -1),\n",
    "            rotate_range=(0, 0, np.pi / 2),\n",
    "            scale_range=(0.1, 0.1),\n",
    "            mode=(\"bilinear\", \"nearest\"),\n",
    "            prob=1.0,\n",
    "        ),\n",
    "    ]\n",
    ")\n",
    "# start a dataset\n",
    "dataset = monai.apps.DecathlonDataset(\n",
    "    root_dir=\"./\", task=\"Task05_Prostate\", section=\"training\", transform=xform, download=True\n",
    ")\n",
    "\n",
    "# start a pytorch dataloader\n",
    "# data_loader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, num_workers=1)\n",
    "data_loader = monai.data.DataLoader(dataset, batch_size=1, shuffle=True, num_workers=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "r8C2LV75vptv"
   },
   "source": [
    "Take a peek at what's happening in the `data_dict` (rerun the following section will generate randomly augmented samples from the dataset):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 185
    },
    "id": "7IIQ8bvTvo97",
    "outputId": "e34a79d9-8def-4ba5-b0bb-8be2f8d70b6e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 2, 64, 64, 32]) torch.Size([1, 1, 64, 64, 32]) ['Task05_Prostate/imagesTr/prostate_40.nii.gz']\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "unique labels: [0. 1. 2.]\n"
     ]
    }
   ],
   "source": [
    "data_dict = next(iter(data_loader))\n",
    "print(data_dict[\"image\"].shape, data_dict[\"label\"].shape, data_dict[\"image\"].meta[\"filename_or_obj\"])\n",
    "\n",
    "plt.subplots(1, 3)\n",
    "plt.subplot(1, 3, 1)\n",
    "plt.imshow(data_dict[\"image\"][0, 0, ..., 16])\n",
    "plt.subplot(1, 3, 2)\n",
    "plt.imshow(data_dict[\"image\"][0, 1, ..., 16])\n",
    "plt.subplot(1, 3, 3)\n",
    "plt.imshow(data_dict[\"label\"][0, 0, ..., 16])\n",
    "plt.show()\n",
    "\n",
    "print(f\"unique labels: {np.unique(data_dict['label'])}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1lGzy62hpPe4"
   },
   "source": [
    "The data loader has added a batch dimension to both image and label, so that they are ready for the deep learning models.\n",
    "\n",
    "Because \"nearest\" interpolation mode is used for the label, the number of unique classes is preserved during the transforms."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Ru4U1eCdVR4M"
   },
   "source": [
    "## Layers, blocks, networks and loss functions\n",
    "\n",
    "We've gone through the data input pipeline. the properly preprocessed data are ready for the 3-class segmentation task using two modalities inputs.\n",
    "\n",
    "This section creates a UNet model, as well as a loss function and optimizer. \n",
    "\n",
    "All these modules are direct uses or extensions of the PyTorch interfaces (such as `torch.nn.Module`). They could be replaced by any customized Python code, as long as the code also follows the standard PyTorch APIs.\n",
    "\n",
    "MONAI's modules focus on providing enhanced modules for medical images analysis:\n",
    "- implements reference networks with the aims of both flexibility and code readability\n",
    "- predefined layers and blocks which are compatible with 1D, 2D and 3D networks\n",
    "- domain-specific loss functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "id": "ns1P9hNNVZU5"
   },
   "outputs": [],
   "source": [
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "net = monai.networks.nets.UNet(\n",
    "    spatial_dims=3,\n",
    "    in_channels=2,\n",
    "    out_channels=3,\n",
    "    channels=(16, 32, 64, 128, 256),\n",
    "    strides=(2, 2, 2, 2),\n",
    "    num_res_units=2,\n",
    "    norm=monai.networks.layers.Norm.BATCH,\n",
    ").to(device)\n",
    "loss = monai.losses.DiceLoss(to_onehot_y=True, softmax=True)\n",
    "opt = torch.optim.Adam(net.parameters(), 1e-2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "y9cIBMY5ktcF"
   },
   "source": [
    "`UNet` is defined with a `dimensions` parameter. It specifies the number of spatial dimensions of the pytorch APIs, such as Conv1d, Conv2d and Conv3d. With the same implementation, it could be easily configured for 1D, 2D, 3D, and multi-modal training.\n",
    "\n",
    "`UNet` as an extension of `torch.nn.Module` is a set of MONAI blocks and layers organized in a specific way. The blocks and layers, such as commonly used combinations of \"Conv. + Feature Norm. + Activation\", are designed as reusable submodules."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "XENK4QqZXeIl"
   },
   "source": [
    "## Sliding window inference\n",
    "For this commonly used module, MONAI provides a simple PyTorch based implementation, which only requires specifications of window size and a input model (which could be a `torch.nn.Module` implementation)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "69wwB8teaXdA"
   },
   "source": [
    "The following demo shows you a toy of sliding window inference on an input image of (1, 1, 200, 200) by aggregating (40, 40) spatial size windows running through all the spatial locations.\n",
    "\n",
    "You can change the `roi_size`, `sw_batch_size`, and `overlap` parameters to see their impacts on the sliding window outputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "pYDG9BPHXlvC"
   },
   "outputs": [],
   "source": [
    "class ToyModel:\n",
    "    # A simple model generates the output by adding an integer `pred` to input.\n",
    "    # each call of this instance increases the integer by 1.\n",
    "    pred = 0\n",
    "\n",
    "    def __call__(self, input):\n",
    "        self.pred = self.pred + 1\n",
    "        input = input + self.pred\n",
    "        return input\n",
    "\n",
    "\n",
    "infer = monai.inferers.SlidingWindowInferer(roi_size=(40, 40), sw_batch_size=1, overlap=0)\n",
    "input_tensor = torch.zeros(1, 1, 200, 200)\n",
    "output_tensor = infer(input_tensor, ToyModel())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 269
    },
    "id": "xcP4zo65Y7m1",
    "outputId": "599e86e7-cdda-433c-eb1a-9b24c80920d1"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(output_tensor[0, 0])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "meGyNoAbPYOu"
   },
   "source": [
    "## Start a training workflow (with validations at every epoch)\n",
    "\n",
    "The input pipeline, network architectures, loss functions are all compatible with any existing pytorch-based workflow. Please see the tutorials for different use cases:\n",
    "https://github.com/Project-MONAI/tutorials\n",
    "\n",
    "- [Array-based U-Net training example](../3d_segmentation/torch/unet_training_array.py)\n",
    "- [Dict-based U-Net training example](../3d_segmentation/torch/unet_training_dict.py)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "7xw-_DrKSZZz"
   },
   "source": [
    "\n",
    "Here we would like to highlight the workflow utilities, built by MONAI based on PyTorch-Ignite.  The main goal is to reduce user's efforts when building relatively standard training workflows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "kQnDhk3LPXj1",
    "outputId": "43c9bb54-b8e8-454b-8a90-3887fd3af246"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.4.10\n"
     ]
    }
   ],
   "source": [
    "print(ignite.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "kvn_6mf9gZoA"
   },
   "source": [
    "The following commands will start a `SupervisedTrainer` instance. As an extension of Pytorch ignite's facilities, it combines all the elements mentioned before. Calling `trainer.run()` will train the network for two epochs and compute `MeanDice` metric based on the training data at the end of every epoch.\n",
    "\n",
    "The `key_train_metric` is used to track the progress of model quality improvement. Additional handlers could be set to do early stopping and learning rate scheduling.\n",
    "\n",
    "The `StatsHandler` is triggered at each training iteration, to print diagnostic information to stdout.  These could be configured to generate detailed training logs at a user-specified frequency.\n",
    "\n",
    "For more information about the event handling system, please refer to the documentation https://docs.monai.io/en/latest/handlers.html.\n",
    "\n",
    "It is worth noting that these features facilitate fast prototyping of regular training and validation pipelines.  The users can always choose to build their own pipelines, benefiting from the modules mentioned in the previous sections."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "bRLOKFnLvm3c"
   },
   "outputs": [],
   "source": [
    "trainer = monai.engines.SupervisedTrainer(\n",
    "    device=device,\n",
    "    max_epochs=2,\n",
    "    train_data_loader=data_loader,\n",
    "    network=net,\n",
    "    optimizer=opt,\n",
    "    loss_function=loss,\n",
    "    inferer=monai.inferers.SlidingWindowInferer((32, 32, -1), sw_batch_size=2),  # optionally using a sw inferer\n",
    "    postprocessing=mt.AsDiscreteD(keys=[\"pred\", \"label\"], argmax=(True, False), to_onehot=3),\n",
    "    key_train_metric={\n",
    "        \"train_meandice\": monai.handlers.MeanDice(output_transform=monai.handlers.from_engine([\"pred\", \"label\"]))\n",
    "    },\n",
    "    train_handlers=monai.handlers.StatsHandler(\n",
    "        tag_name=\"train_loss\", name=\"Training\", output_transform=monai.handlers.from_engine([\"loss\"], first=True)\n",
    "    ),\n",
    ")\n",
    "trainer.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "19T1jA1m6BUY"
   },
   "source": [
    "## Deterministic training\n",
    "Deterministic training support is important for reproducible research.\n",
    "MONAI currently provides two mechanisms:\n",
    "\n",
    "1. set the random state of all the random transforms. This will not affect the global random state. For example:\n",
    "```python\n",
    "# define a transform chain for pre-processing\n",
    "train_transforms = monai.transforms.Compose([\n",
    "    LoadImaged(keys=['image', 'label']),\n",
    "    RandRotate90d(keys=['image', 'label'], prob=0.2, spatial_axes=[0, 2]),\n",
    "    ... ...\n",
    "])\n",
    "# set determinism for reproducibility\n",
    "train_transforms.set_random_state(seed=0)\n",
    "```\n",
    "\n",
    "2. enable deterministic training globally for python, numpy and pytorh in one line of code, for example:\n",
    "``` \n",
    "monai.utils.set_determinism(seed=0, additional_settings=None)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "riewzpdHc12h"
   },
   "source": [
    "## Summary\n",
    "\n",
    "We have walked through the major modules in MONAI. By making highly flexible extensions and wrappers, MONAI enhances the pytorch APIs from a medical image analysis perspective.\n",
    "\n",
    "For details of our latest milestone highlights, please visit https://docs.monai.io/en/latest/highlights.html.\n",
    "\n",
    "To dive into MONAI, the tutorials repository is a good starting point https://github.com/Project-MONAI/tutorials.\n",
    "\n",
    "For API documentation, please visit https://docs.monai.io/en/stable/api.html."
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
